package client

import (
	"errors"
	"gitee.com/cooge/impush/config"
	"gitee.com/cooge/impush/message"
	"gitee.com/cooge/impush/stream"
	"gitee.com/cooge/impush/user"
	"gitee.com/cooge/impush/util"
	log "github.com/sirupsen/logrus"
	"net"
	"strconv"
	"time"
)

const (
	CLUSTER_CLIENT_START_SUCCESS = 2
	CLUSTER_CLIENT_NET_BREAK     = -2
	CLUSTER_CLIENT_START_ERROR   = -3
	CLUSTER_CLIENT_SEND_MESSAGE  = 1
	CLUSTER_CLIENT_SEND_USER     = 4
)

type ClusterClientHandleFunc func(*ClusterClient, *message.Message, int) error

type ClusterClient struct {
	*user.UserLite
	ms                      *stream.MessageStream
	Raddress                *net.TCPAddr
	clusterClientHandleFunc ClusterClientHandleFunc
	reTryNum                int
}

func NewClusterClient1(raddress *net.TCPAddr) *ClusterClient {
	var cc ClusterClient
	cc.reTryNum = 5
	cc.Raddress = raddress
	return &cc
}
func NewClusterClient2(raddress string) *ClusterClient {
	tcpAddr, err := net.ResolveTCPAddr("tcp4", raddress)
	if err != nil {
		return nil
	}
	var cc ClusterClient
	cc.reTryNum = 5
	cc.Raddress = tcpAddr
	return &cc
}

func (cc *ClusterClient) Start(clusterClientHandleFunc ClusterClientHandleFunc) {
	cc.clusterClientHandleFunc = clusterClientHandleFunc
	cc.xstart()
}

func (cc *ClusterClient) xstart() {
	var times = 0
	for {
		times++
		log.Info("尝试连接集群服务器", cc.Raddress, "中", times)
		err := cc.start()
		if err == nil {
			laddr := cc.ms.Conn.LocalAddr().(*net.TCPAddr)
			if laddr.IP.String() == cc.Raddress.IP.String() && config.CLUSTER_LOCAL_PORT == cc.Raddress.Port {
				log.Info("连接到自身强行关闭", cc.Raddress)
				cc.reTryNum = 0
				cc.close()
				break
			} else {
				log.Info("连接集群服务器", cc.Raddress, "成功", times)
				cc.clusterClientHandleFunc(cc, nil, CLUSTER_CLIENT_START_SUCCESS)
				break
			}
		} else {
			log.Info("尝试连接集群服务器", cc.Raddress, "次失败", times)
			if times >= cc.reTryNum {
				log.Info("尝试连接集群服务器", cc.Raddress, "失败", times,"目标机器已经掉线")
				cc.clusterClientHandleFunc(cc, nil, CLUSTER_CLIENT_START_ERROR)
				break
			}
			time.Sleep(time.Duration(3) * time.Second)
		}
	}

}

func (this *ClusterClient) close() {
	this.ms.Conn.Close()
}

func (cc *ClusterClient) start() error {
	conn, err := net.DialTCP("tcp4", nil, cc.Raddress)
	if err == nil {
		cc.ms = stream.NewMessageStream(conn)
		go cc.read()
		return nil
	} else {
		log.Info("CONN REMOTE  ERROR", cc.Raddress, err)
		return err
	}
}

func (this *ClusterClient) write(msg []byte) (int, error) {
	return this.ms.Conn.Write(msg)
}
func (this *ClusterClient) Write(msg []byte) (int, error) {
	return this.write(msg)
}

func (this *ClusterClient) Live(address []string) (int, error) {
	address = util.RemoveArrays(address, this.Raddress.String())
	addr, ok := this.ms.Conn.LocalAddr().(*net.TCPAddr)
	if ok {
		address = append(address, util.Merge(addr.IP.String(), ":", strconv.Itoa(config.CLUSTER_LOCAL_PORT)))
		msg := message.SendClusterLiveMessage(address)
		return this.Write(msg)
	}
	return 0, errors.New("LOCAL ADDRSSS ERROR")
}

func (cc *ClusterClient) read() {
	for {
		rms, err := cc.ms.ReadMessage()
		if err == nil {
			cc.handle(rms)
		} else {
			log.Debug("REMOTE CONN BREAK", cc.Raddress)
			break
		}
	}
	cc.ms.Conn.Close()
	if cc.reTryNum > 0 {
		go cc.xstart()
	} else {
		cc.clusterClientHandleFunc(cc, nil, CLUSTER_CLIENT_NET_BREAK)
	}

}

func (cc *ClusterClient) handle(msg *message.Message) {
	ty := msg.Heads.Get(message.TYPE)
	switch ty {
	case message.CLUSTER:
		{
			action := msg.Heads.Get(message.ACTION)
			switch action {
			case message.LIVE:
				{
					log.Debug("收到集群服务端的心跳回馈")
				}
			}
		}
	case message.MESSAGE:
		{
			touser := msg.Heads.Get(message.TO)
			err :=cc.clusterClientHandleFunc(cc, msg, CLUSTER_CLIENT_SEND_MESSAGE)
			if err != nil {
				cc.Write(message.SendMachinesNoUser(touser))
			}

		}
	case message.ALL_MESSAGE:
		{
			msg.Heads[message.TYPE] = message.MESSAGE
			touser := msg.Heads.Get(message.TO)
			err := cc.clusterClientHandleFunc(cc, msg, CLUSTER_CLIENT_SEND_MESSAGE)
			if err != nil {
				cc.Write(message.SendMachinesNoUser(touser))
			} else {
				log.Debug("同步用户", touser)
				cc.clusterClientHandleFunc(cc, msg, CLUSTER_CLIENT_SEND_USER)
			}

		}
	}
}
